/*!
  \file
  File to hold an automatic object convertor
*/

/*
  Copyright (c) 2011, President & Fellows of Harvard College
  All rights reserved.
  
  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  
  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  * Neither the name of the Harvard College nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/



#ifndef SCIGPU_HHV_OBJECT_CONVERTOR_HPP
#define SCIGPU_HHV_OBJECT_CONVERTOR_HPP

#include <iostream>
#include <typeinfo>
#include <exception>
#include <stdexcept>
#include <cstdlib>

namespace SciGPU {
  namespace HHV {

    //! Object conversion abstraction
    /*!
      This class is used to abstract the conversion of classes
      derived from a common base class.
      For the interconversion to work, the base class must provide
      a \c Copy method.
      In the case of the heterogeneous hypervolumes, it is used by
      implementations of algorithms to convert their inputs (which
      should be of abstract type SciGPU::HHV::HHVolume) into their
      desired implementation types.

      Two constructors are provided, from \c const and non-\c const
      inputs.
      If the runtime type of the supplied input is not the desired
      implementation type, then an internal copy will be made (which
      is deleted on object destruction).
      Two accessors are provided for \c const and non-\c const references
      to an object of the desired implementation type.
      If an object was constructed from a \c const source, an exception
      of type \c std::runtime_error will be thrown if a non-\c const reference
      is requested.
      @tparam BaseClass The base class for the conversion
      @tparam DerivedClass The desired implementation class for the conversion
      @bug Should probably have our own exceptions for this
     */
    template<typename DerivedClass, typename BaseClass>
    class ObjectConvertor {
    public:

 

      //! Constructor from mutable object
      ObjectConvertor( BaseClass& src, const bool copySrc = true )
        : srcMutable(&src),
          srcConst(NULL),
          deriveMutable(NULL),
          deriveConst(NULL) {
        /*!
          This constructor is used for mutable objects.
          If the runtime type of the \a src object is not
          \a DerivedClass, then a new object of type \a DerivedClass
          will be constructed.
          If specified by \a copySrc, data from the \a src
          object will be copied to the new \a DerivedClass object.
          On destruction, data from the \a DerivedClass object
          will be copied back to the \a src object
          (if the original source was not of the desired
          implementation type).
         */
        try {
          this->deriveMutable = &dynamic_cast<DerivedClass&>(src);
        }
        catch( std::bad_cast& e ) {
          this->deriveMutable = new DerivedClass;
          if( copySrc ) {
            this->deriveMutable->Copy( src );
          }
        }
      };

      //! Constructor from const object
      ObjectConvertor( const BaseClass& src ) : srcMutable(NULL),
                                                srcConst(&src),
                                                deriveMutable(NULL),
                                                deriveConst(NULL) {
        /*!
          This constructor is used for \c const objects.
          If necessary, an object of the desired \a DerivedClass
          is created
         */
        DerivedClass *tmp;
        try {
          this->deriveConst = &dynamic_cast<const DerivedClass&>(src);
        } 
        catch( std::bad_cast& e ) {
          tmp = new DerivedClass;
          tmp->Copy( src );
          this->deriveConst = tmp;
        }
      };


      //! Destructor
      virtual ~ObjectConvertor( void ) {
        // If we had to convert a const, just delete
        if( deriveConst != srcConst ) {
          delete deriveConst;
        }
        // If we had a mutable, need to copy first
        if( deriveMutable != srcMutable ) {
          srcMutable->Copy( *deriveMutable );
          delete deriveMutable;
        }
      }

  
      //! Operator to get const object
      const DerivedClass& getConst( void ) const {
        const DerivedClass* dccr = NULL;

        if( this->deriveConst != NULL ) {
          dccr = this->deriveConst;
        } else if( this->deriveMutable != NULL ) {
          dccr = this->deriveMutable;
        } else {
          throw std::runtime_error( "Source not available" );
        }

        return( *dccr );
      }

      //! Operator to get mutable object
      DerivedClass& get( void ) const {
        if( this->deriveMutable == NULL ) {
          throw std::runtime_error( "Source not mutable" );
        }
        return( *(this->deriveMutable) );
      }

    private:
      //! Pointer to a mutable source
      BaseClass *srcMutable;
      //! Pointer to a const source
      const BaseClass *srcConst;
      //! Pointer to a mutable derived object
      DerivedClass *deriveMutable;
      //! Pointer to a const derived object
      const DerivedClass *deriveConst;

      //! Default construction not allowed
      ObjectConvertor( void ) : srcMutable(NULL),
                                srcConst(NULL),
                                deriveMutable(NULL),
                                deriveConst(NULL) {};

      //! Copy construction not allowed
      ObjectConvertor( const ObjectConvertor& src ) : srcMutable(NULL),
                                                      srcConst(NULL),
                                                      deriveMutable(NULL),
                                                      deriveConst(NULL) {};

      //! Assignment not allowed
      ObjectConvertor& operator=( const ObjectConvertor& src ) const {
        std::cerr << __FUNCTION__
                  << ": Assignment operator not allowed"
                  << std::endl;
        abort();
        return( *this );
      }

    };


  }
}


#endif
